package com.nlp.server.filter;

import com.alibaba.fastjson2.JSONObject;
import com.nlp.common.core.constant.CacheConstants;
import com.nlp.common.core.constant.SecurityConstants;
import com.nlp.common.core.constant.TokenConstants;
import com.nlp.common.core.result.Result;
import com.nlp.common.core.utils.ServletUtils;
import com.nlp.common.core.utils.jwt.JwtUtils;
import com.nlp.common.redis.service.RedisService;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import com.nlp.common.core.utils.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

/**
 * 鉴权
 * 
 * @author shiliuyinzhen
 */
@Order(-1)
@WebFilter(urlPatterns = "/**")
@Component
public class AuthFilter implements Filter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);

    @Autowired
    private RedisService redisService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        //前置：强制转换为http协议的请求对象、响应对象 （转换原因：要使用子类中特有方法）
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        //1.获取请求url
        String url = request.getRequestURL().toString();

        //2.判断请求url中是否包含login，如果包含，说明是登录操作，放行
        //排除接口文档
        if (url.contains("/login") || url.contains("/register")
                || url.contains("/doc.html")
                || url.contains("/swagger-ui.html") || url.contains("/swagger-resources")
                || url.contains("/v2/api-docs") || url.contains("/webjars/")
                || url.contains("/swagger-resources/configuration/ui")
                || url.contains("/swagger-resources/configuration/security")
                || url.contains("/favicon.ico") || url.contains("/baidu")){
            //Todo 测试sse连接时使用
            // || url.contains("/sse") || url.contains("/chat")
            chain.doFilter(request, response); // 放行请求
            return;
        }

        //3.获取请求头中的令牌（token）
        String token = getToken(request);
        //4.判断令牌是否存在，如果不存在，返回错误结果（未登录）
        if (StringUtils.isEmpty(token)) {
            unauthorizedResponse(url,response,"令牌不能为空");
            return;
        }
        Claims claims = JwtUtils.parseToken(token);
        if (claims == null) {
            unauthorizedResponse(url,response, "令牌已过期或验证不正确！");
            return;
        }
        String userKey = JwtUtils.getUserKey(claims);
        boolean isLogin = redisService.hasKey(getTokenKey(userKey));
        if (!isLogin) {
            unauthorizedResponse(url,response, "登录状态已过期");
            return;
        }
        String userId = JwtUtils.getUserId(claims);
        String userName = JwtUtils.getUserName(claims);
        if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(userName)) {
            unauthorizedResponse(url,response, "令牌验证失败");
            return;
        }
        HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(request);

        // 设置用户信息到请求
        addHeader(requestWrapper,SecurityConstants.USER_KEY, userKey);
        addHeader(requestWrapper,SecurityConstants.DETAILS_USER_ID, userId);
        addHeader(requestWrapper,SecurityConstants.DETAILS_USERNAME, userName);

        //6.放行
        chain.doFilter(requestWrapper, response);
    }

    /**
     * 添加请求头信息
     * @param requestWrapper 请求头包装类
     * @param name 参数名
     * @param value 参数值
     */
    private void addHeader(HeaderMapRequestWrapper requestWrapper, String name, Object value) {
        if (value == null) {
            return;
        }
        String valueStr = value.toString();
        String valueEncode = ServletUtils.urlEncode(valueStr);
        requestWrapper.addHeader(name,valueEncode);
    }

    /**
     * 获取缓存key
     */
    private String getTokenKey(String token)
    {
        return CacheConstants.LOGIN_TOKEN_KEY + token;
    }

    /**
     * 获取请求token
     */
    private String getToken(HttpServletRequest request) {
        String token = request.getHeader(TokenConstants.AUTHENTICATION);
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
            token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
        }
        return token;
    }

    /**
     * 返回鉴权异常响应
     * @param response
     * @param msg
     * @throws IOException
     */
    public void unauthorizedResponse(String url,HttpServletResponse response, String msg) throws IOException {
        log.error("[鉴权异常处理]请求路径:{}", url);
        Result responseResult = Result.fail(msg);
        //把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
        String json = JSONObject.toJSONString(responseResult);
        response.setContentType("application/json;charset=utf-8");
        //响应
        response.getWriter().write(json);
    }

    @Override
    public int getOrder()
    {
        return -200;
    }
}